/*
 *  IPAddress.c
 *  arp
 *
 *  Created by James Richardson on 3/24/11.
 *  Copyright 2011 Home. All rights reserved.
 *
 */

#include "NetworkInfo.h"

#include <sys/param.h>
#include <sys/file.h>
#include <sys/socket.h>
#include <sys/sysctl.h>
#include <sys/sockio.h>
#include <sys/ioctl.h>
#include <sys/types.h>

#include <net/if.h>
#include <net/if_dl.h>
#include <net/if_types.h>
#include <net/route.h>

#include <netinet/in.h>
#include <netinet/if_ether.h>
#include <net/if_dl.h>
#include <net/ethernet.h>

#include <arpa/inet.h>

#include <err.h>
#include <errno.h>
#include <netdb.h>
#include <nlist.h>
#include <paths.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <string.h>


#define	min(a,b)	((a) < (b) ? (a) : (b))
#define	max(a,b)	((a) > (b) ? (a) : (b))

#define BUFFERSIZE	4000

char *if_names[MAXADDRS];
char *ip_names[MAXADDRS];
char *hw_addrs[MAXADDRS];
unsigned long ip_addrs[MAXADDRS];

char *host_host[MAXADDRS];
char *ip_host[MAXADDRS];
char *hw_host[MAXADDRS];

int staticARP;





//static int   nextAddr = 0;
static int	 nflag;


void InitAddresses()
{
	int i;
	for (i=0; i<MAXADDRS; ++i)
	{
		if_names[i] = ip_names[i] = hw_addrs[i] = host_host[i] = ip_host[i] = hw_host[i] = NULL;
		ip_addrs[i] = 0;
	}
    
}

void FreeAddresses_arp()
{
    
	int i;
	for (i=0; i<MAXADDRS; i++)
	{
        //printf("IT: %d \n",i);
        
		if (if_names[i] != 0) free(if_names[i]);
		if (ip_names[i] != 0) free(ip_names[i]);
		if (hw_addrs[i] != 0) free(hw_addrs[i]);
        
       // if(!staticARP){
        
            if (host_host[i] != 0) free(host_host[i]);
            if (ip_host[i] != 0) free(ip_host[i]);
            if (hw_host[i] != 0) free(hw_host[i]);
            
            
       // }

		ip_addrs[i] = 0; 
        
	}
    
   /* if(staticARP){
        
        if (host_host[0] != 0) free(host_host[0]);
        if (ip_host[0] != 0) free(ip_host[0]);
        if (hw_host[0] != 0) free(hw_host[0]);
        
        staticARP = 0;
    }*/
    
        
        
	InitAddresses();
}

void FreeAddresses_arp_noNet(){
    /*int i;
	for (i=0; i<MAXADDRS; i++)
	{
        if (host_host[i] != 0) free(host_host[i]);
        if (ip_host[i] != 0) free(ip_host[i]);
        if (hw_host[i] != 0) free(hw_host[i]);
    }*/
    
    if (host_host[0] != 0) free(host_host[0]);
    if (ip_host[0] != 0) free(ip_host[0]);
    if (hw_host[0] != 0) free(hw_host[0]);
}

void FreeAddresses_ip()
{
    
	int i;
	for (i=0; i<MAXADDRS; ++i)
	{
		if (if_names[i] != 0) free(if_names[i]);
		if (ip_names[i] != 0) free(ip_names[i]);
		if (hw_addrs[i] != 0) free(hw_addrs[i]);
        
		ip_addrs[i] = 0;

	}
	InitAddresses();
}

void GetIPAddresses()
{
	int                 i, len, flags;
	char                buffer[BUFFERSIZE], *ptr, lastname[IFNAMSIZ], *cptr;
	struct ifconf       ifc;
	struct ifreq        *ifr, ifrcopy;
	struct sockaddr_in	*sin;
	
	int   nextAddr = 0;
	
	char temp[80];
	
	int sockfd;
	
    
	
	for (i=0; i<MAXADDRS; ++i)
	{
		if_names[i] = ip_names[i] = NULL;
		ip_addrs[i] = 0;
	}
	
	
	sockfd = socket(AF_INET, SOCK_DGRAM, 0);
	if (sockfd < 0)
	{
		perror("socket failed");
		return;
	}
	
	ifc.ifc_len = BUFFERSIZE;
	ifc.ifc_buf = buffer;
	
	if (ioctl(sockfd, SIOCGIFCONF, &ifc) < 0)
	{
		perror("ioctl error");
		return;
	}
	
    
	lastname[0] = 0;
	
	
	
	for (ptr = buffer; ptr < buffer + ifc.ifc_len; )
	{
		
		ifr = (struct ifreq *)ptr;
		len = max(sizeof(struct sockaddr), ifr->ifr_addr.sa_len);
		ptr += sizeof(ifr->ifr_name) + len;	// for next one in buffer
		
		if (ifr->ifr_addr.sa_family != AF_INET)
		{
			continue;	// ignore if not desired address family
		}
		
		if ((cptr = (char *)strchr(ifr->ifr_name, ':')) != NULL)
		{
			*cptr = 0;		// replace colon will null
		}
		
		if (strncmp(lastname, ifr->ifr_name, IFNAMSIZ) == 0)
		{
			continue;	/* already processed this interface */
		}
		
		memcpy(lastname, ifr->ifr_name, IFNAMSIZ);
        
		
		ifrcopy = *ifr;
		ioctl(sockfd, SIOCGIFFLAGS, &ifrcopy);
		flags = ifrcopy.ifr_flags;
		if ((flags & IFF_UP) == 0)
		{
			continue;	// ignore if interface not up
		}
		
		
		if_names[nextAddr] = (char *)malloc(strlen(ifr->ifr_name)+1);
		if (if_names[nextAddr] == NULL)
		{
			return;
		}
		strcpy(if_names[nextAddr], ifr->ifr_name);
		
		sin = (struct sockaddr_in *)&ifr->ifr_addr;
		strcpy(temp, inet_ntoa(sin->sin_addr));
		
		ip_names[nextAddr] = (char *)malloc(strlen(temp)+1);
		if (ip_names[nextAddr] == NULL)
		{
			return;
		}
		strcpy(ip_names[nextAddr], temp);
		
		ip_addrs[nextAddr] = sin->sin_addr.s_addr;
        
		++nextAddr;
	}
	
	close(sockfd);
}

void GetHWAddresses()
{
	struct ifconf ifc;
	struct ifreq *ifr;
	int i, sockfd;
	char buffer[BUFFERSIZE], *cp, *cplim;
	char temp[80];
	
	
	
	for (i=0; i<MAXADDRS; ++i)
	{
		hw_addrs[i] = NULL;
	}
	
	sockfd = socket(AF_INET, SOCK_DGRAM, 0);
	if (sockfd < 0)
	{
		perror("socket failed");
		return;
	}
	
	ifc.ifc_len = BUFFERSIZE;
	ifc.ifc_buf = buffer;
	
	if (ioctl(sockfd, SIOCGIFCONF, (char *)&ifc) < 0)
	{
		perror("ioctl error");
		close(sockfd);
		return;
	}
	
	//ifr = ifc.ifc_req;
	
	cplim = buffer + ifc.ifc_len;
	
	for (cp=buffer; cp < cplim; )
	{
		ifr = (struct ifreq *)cp;
		if (ifr->ifr_addr.sa_family == AF_LINK)
		{
			struct sockaddr_dl *sdl = (struct sockaddr_dl *)&ifr->ifr_addr;
			int a,b,c,d,e,f;
			int i;
			
			
			strcpy(temp, (char *)ether_ntoa(LLADDR(sdl)));
			sscanf(temp, "%x:%x:%x:%x:%x:%x", &a, &b, &c, &d, &e, &f);
			sprintf(temp, "%02X:%02X:%02X:%02X:%02X:%02X",a,b,c,d,e,f);
			
			for (i=0; i<MAXADDRS; ++i)
			{
				if ((if_names[i] != NULL) && (strcmp(ifr->ifr_name, if_names[i]) == 0))
				{
					if (hw_addrs[i] == NULL)
					{
						hw_addrs[i] = (char *)malloc(strlen(temp)+1);
						strcpy(hw_addrs[i], temp);
						break;
					}
				}
			}
		}
		cp += sizeof(ifr->ifr_name) + max(sizeof(ifr->ifr_addr), ifr->ifr_addr.sa_len);
	}
	
	close(sockfd);
}


void
ether_print(cp)
u_char *cp;
{
    printf("%x:%x:%x:%x:%x:%x", cp[0], cp[1], cp[2], cp[3], cp[4], cp[5]);
}


/*
 * Dump the entire arp table
 */
int
dump(addr)
u_long addr;
{
	
    int mib[6];
    size_t needed;
    char *host, *lim, *buf, *next;
    struct rt_msghdr *rtm;
    struct sockaddr_inarp *sin;
    struct sockaddr_dl *sdl;
    extern int h_errno;
    struct hostent *hp;
    //int found_entry = 0;
	
    mib[0] = CTL_NET;
    mib[1] = PF_ROUTE;
    mib[2] = 0;
    mib[3] = AF_INET;
    mib[4] = NET_RT_FLAGS;
    mib[5] = RTF_LLINFO;
	
    if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
        err(1, "route-sysctl-estimate");
    if ((buf = malloc(needed)) == NULL)
        err(1, "malloc");
    if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0)
        err(1, "actual retrieval of routing table");
    

    
    lim = buf + needed;
	
    
    
	if(lim == buf)
	{
		
		char msg[] = {'N','O',' ','N','E','T','W','O','R','K',' ','C','O','N','N','E','C','T','I','O','N','!'};
        
		host_host[0] = (char *)malloc(strlen(msg));
		strcpy(host_host[0], msg);
		
		ip_host[0] = (char *)malloc(strlen(msg));
		strcpy(ip_host[0], msg);
		
		hw_host[0] = (char *)malloc(strlen(msg));
		strcpy(hw_host[0], msg);
        
        staticARP = 1;
        
	}
	int i = 0;
    for (next = buf; next < lim; next += rtm->rtm_msglen) {
        
        rtm = (struct rt_msghdr *)next;
        sin = (struct sockaddr_inarp *)(rtm + 1);
        sdl = (struct sockaddr_dl *)(sin + 1);
		
        if (addr) {
            if (addr != sin->sin_addr.s_addr)
                continue;
        }
		
        if (nflag == 0)
            hp = gethostbyaddr((caddr_t)&(sin->sin_addr),sizeof sin->sin_addr, AF_INET);
        else
            hp = 0;
		
        if (hp)
            host = hp->h_name;
        else {
            host = "?";
            if (h_errno == TRY_AGAIN)
                nflag = 1;
        }
		
		//PRINT HOST AND IP
        
		char tmp[80];
		
		strcpy(tmp,host);
		host_host[i] = (char *)malloc(strlen(tmp)+1);
		strcpy(host_host[i], tmp);
		
		strcpy(tmp, inet_ntoa(sin->sin_addr));
		ip_host[i] = (char *)malloc(strlen(tmp)+1);
		strcpy(ip_host[i], tmp);
		
		
		
		//PRINT ETHER (OR STATUS)
        if (sdl->sdl_alen){
            //ether_print((u_char *)LLADDR(sdl));
			int a,b,c,d,e,f;
            
			strcpy(tmp, (char *)ether_ntoa(LLADDR(sdl)));
			sscanf(tmp, "%x:%x:%x:%x:%x:%x", &a, &b, &c, &d, &e, &f);
			sprintf(tmp, "%02X:%02X:%02X:%02X:%02X:%02X",a,b,c,d,e,f);
			
			hw_host[i] = (char *)malloc(strlen(tmp)+1);
			strcpy(hw_host[i], tmp);
            
            
            
		}
        else{

			char msg[] = {'(','i','n','c','o','m','p','l','e','t','e',')','\0'};
			
			hw_host[i] = (char *)malloc(strlen(msg)+1);
			strcpy(hw_host[i], msg);
            
            
			
		}
		
        if (rtm->rtm_rmx.rmx_expire == 0){

			char msg[] = {'p','e','r','m','a','n','e','n','t','\0'};
			
            free(hw_host[i]);
            

            
			hw_host[i] = (char *)malloc(strlen(msg));
			strcpy(hw_host[i], msg);
            
            
		}
		
        if (sin->sin_other & SIN_PROXY){

			
			char msg[] = {'p','u','b','l','i','s','h','e','d',' ','(','p','r','o','x','y',' ','o','n','l','y',')','\0'};
			
            free(hw_host[i]);
            
			hw_host[i] = (char *)malloc(strlen(msg));
			strcpy(hw_host[i], msg);
            
           
		}
		
        if (rtm->rtm_addrs & RTA_NETMASK) {
            sin = (struct sockaddr_inarp *)
			(sdl->sdl_len + (char *)sdl);
            if (sin->sin_addr.s_addr == 0xffffffff){

				
                free(hw_host[i]);
                
				char msg[] = {'p','u','b','l','i','s','h','e','d','\0'};
				
				hw_host[i] = (char *)malloc(strlen(msg));
				strcpy(hw_host[i], msg);
			}
            if (sin->sin_len != 8){
				
                free(hw_host[i]);
                
				char msg[] = {'(','w','e','i','r','d',')','\0'};
				
				hw_host[i] = (char *)malloc(strlen(msg));
				strcpy(hw_host[i], msg);
				
			}
        }
		
		i++;
    }
    
    free(buf);
	
	return i;
}
